package org.infodavid.common.util;

import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.util.Arrays;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * The Class ArraysUtil.
 */
public final class ArraysUtil {

	/** The Constant log. */
	private static final Log LOGGER = LogFactory.getLog(ArraysUtil.class);

	/** The singleton. */
	private static WeakReference<ArraysUtil> singleton = null;

	/**
	 * returns the singleton.
	 * @return <code>ArraysUtil</code>
	 */
	public static synchronized ArraysUtil getInstance() {
		if (singleton == null || singleton.get() == null) {
			singleton = new WeakReference<ArraysUtil>(new ArraysUtil());
		}

		return singleton.get();
	}

	/**
	 * To string.
	 * @param arrays the arrays
	 * @param buffer the buffer
	 * @return the string
	 */
	public static String toString(final double[][] arrays, final StringBuffer buffer) {
		if (arrays != null) {
			buffer.append('[');

			for (final double[] array : arrays) {
				buffer.append(Arrays.toString(array));
			}

			buffer.append(']');
		}

		return buffer.toString();
	}

	/** The is debug enabled. */
	private final boolean isDebugEnabled = LOGGER.isDebugEnabled();

	/**
	 * Instantiates a new arrays utility.
	 */
	private ArraysUtil() {
		super();
	}

	/**
	 * Removes the ending nulls.
	 * @param <T> the generic type
	 * @param array the array
	 * @return the compacted array
	 */
	@SuppressWarnings("unchecked")
	public <T> T[] rtrim(final T[] array) {
		T[] values = array;

		if (array == null || array.length == 0) {
			if (isDebugEnabled) {
				LOGGER.debug("Array is empty or null");
			}
		}
		else {
			int index = array.length - 1;

			if (isDebugEnabled) {
				LOGGER.debug("Array to rtrim: " + Arrays.toString(array));
			}

			while (array[index] == null) {
				index--;
			}

			index++;

			if (index < array.length) {
				if (isDebugEnabled) {
					LOGGER.debug("Index used for rtim: " + index);
				}

				values = (T[])Array.newInstance(array.getClass().getComponentType(), index);

				System.arraycopy(array, 0, values, 0, index);
			}

			if (isDebugEnabled) {
				LOGGER.debug("Resulting array: " + Arrays.toString(values));
			}
		}

		return values;
	}

	/**
	 * Sub array.
	 * @param <E> the element type
	 * @param source the source
	 * @param fromIndex the from index
	 * @param pageSize the page size
	 * @return the elements or null if source is null
	 */
	public <E> E[] subArray(final E[] source, final int fromIndex, final int pageSize) {
		if (source == null) {
			return source;
		}

		final int size = source.length;
		final int from;

		if (size == 0) {
			return source;
		}

		if (fromIndex < 0 || fromIndex >= size) {
			from = 0;
		}
		else {
			from = fromIndex;
		}

		int to = from + pageSize;

		if (to <= from || to >= size) {
			to = size - 1;
		}

		return Arrays.copyOfRange(source, from, to);
	}

	/**
	 * To array.
	 * @param <T> the generic type
	 * @param clazz the component class
	 * @param src the source array
	 * @return the t[]
	 */
	@SuppressWarnings("unchecked")
	public <T> T[] toArray(final Class<T> clazz, final Object[] src) {
		final T[] result = (T[])Array.newInstance(clazz, src.length);

		System.arraycopy(src, 0, result, 0, src.length);

		return result;
	}
}
